client_id <- readLines("spotify_client_id.txt")
## Warning in readLines("spotify_client_id.txt"): incomplete final line found on
## 'spotify_client_id.txt'
client_secret <- readLines("spotify_client_secret.txt")
## Warning in readLines("spotify_client_secret.txt"): incomplete final line found
## on 'spotify_client_secret.txt'
get_spotify_token <- function(client_id, client_secret) {
  resp <- request("https://accounts.spotify.com/api/token") |>
    req_auth_basic(client_id, client_secret) |>
    req_body_form(grant_type = "client_credentials") |>
    req_method("POST") |>
    req_perform()
  
  resp_body_json(resp)$access_token
}

token <- get_spotify_token(client_id, client_secret)
get_artists_data <- function(artist_ids, token) {
  artist_chunks <- split(artist_ids, ceiling(seq_along(artist_ids) / 50))
  
  map_dfr(artist_chunks, function(ids_chunk) {
    req <- request("https://api.spotify.com/v1/artists") |>
      req_url_query(ids = paste(ids_chunk, collapse = ",")) |>
      req_headers(Authorization = paste("Bearer", token))
    
    resp <- req_perform(req)
    content <- resp_body_json(resp, simplifyVector = FALSE)
    
    map_dfr(content$artists, function(artist) {
      tibble(
        artist_id = artist$id,
        artist_name = artist$name,
        genres = paste(artist$genres, collapse = ", "),
        popularity = artist$popularity,
        followers = artist$followers$total,
        artist_url = artist$external_urls$spotify
      )
    })
  })
}

get_artist_top_tracks <- function(artist_id, token) {
  url <- paste0("https://api.spotify.com/v1/artists/", artist_id, "/top-tracks?market=US")
  
  req <- request(url) |>
    req_headers(Authorization = paste("Bearer", token))
  
  resp <- req_perform(req)
  content <- resp_body_json(resp, simplifyVector = FALSE)
  
  map_dfr(content$tracks, function(track) {
    tibble(
      track_name = track$name,
      album_name = track$album$name,
      popularity = track$popularity,
      track_duration_ms = track$duration_ms,
      explicit = track$explicit,
      artist_id = artist_id
    )
  })
}

artist_ids <- c(
  "06HL4z0CvFAxyc27GXpf02",  # Taylor Swift
  "1Xyo4u8uXC1ZmMpatF05PJ",  # The Weeknd
  "4q3ewBCX7sLwd24euuV69X",  # Bad Bunny
  "3TVXtAsR1Inumwj472S9r4",  # Drake
  "6qqNVTkY8uBg9cP3Jd7DAH",  # Billie Eilish
  "0Y5tJX1MQlPlqiwlOH1tJY",  # Travis Scott
  "12GqGscKJx3aE4t07u7eVZ",  # Peso Pluma
  "5K4W6rqBFWDnAN6FQUkS6x",  # Kanye West
  "66CXWjxzNUsdJxJ2JdwvnR",  # Ariana Grande
  "2LRoIwlKmHjgvigdNGBHNo"   # Feid
)

artists_data <- get_artists_data(artist_ids, token)
all_tracks <- map_dfr(artist_ids, get_artist_top_tracks, token = token)

# Merge track data with artist names
all_tracks <- left_join(all_tracks, artists_data |> select(artist_id, artist_name), by = "artist_id")

Popularity by Track Name (Color by Explicit)

all_tracks |>
  filter(artist_name == "Taylor Swift") |>
  plot_ly(
        x = ~reorder(track_name, popularity),
        y = ~popularity,
        type = 'bar',
        color = ~explicit,
        colors = c('blue', 'red'),
        text = ~paste("Track:", track_name,
                      "\nArtist:", artist_name,
                      "\nPopularity:", popularity,
                      "\nExplicit:", explicit),
        hoverinfo = "text") |>
  layout(title = "Top Tracks by Popularity",
         xaxis = list(title = "Track Name", tickangle = -45),
         yaxis = list(title = "Popularity"))

Resources:

  1. Revealed: The Top Artists, Songs, Albums, Podcasts, and Audiobooks of 2024 (https://newsroom.spotify.com/2024-12-04/top-songs-artists-podcasts-audiobooks-albums-trends-2024/)

  2. Spotify Web API (https://developer.spotify.com/documentation/web-api)